<!-- author: xuett -->
<!-- date: 2023-06-29 10:48:01 -->
<!-- description: 高德地图 -->
<template>
  <div class="map-container">
    <div id="map" class="map"></div>
  </div>
</template>

<script setup>
/* global AMap*/
import * as THREE from "three";
// import { MeshLineGeometry, MeshLineMaterial } from "meshline";
import shanxi from "@/assets/map/shanxi.json";
import shanxiAll from "@/assets/map/shanxi-all.json";

// THREE相关变量
let camera, scene, renderer;

var map = null;
var shanxi_paths = [],
  shanxiAll_paths = [];
var _conf = {
  topMap: "", // 顶部纹理路径
  topMapRepeat: [0.0001, 0.0001], //顶部纹理平铺UV配置
  topColor: "#0d8ce7", // 顶部颜色，可以与topMap叠加
  borderWidth: 5000, // 描边宽度
  borderColor: "#10ecda", // 描边纹理颜色
  borderMap: undefined, //描边纹理路径
  animate: false // 是否支持边界线动画，指定了borderMap才会生效，默认值 false
};
// var _borderMaterial = null;

onMounted(() => {
  initMap();
});

// 初始化地图
const initMap = () => {
  if (map) {
    map.destroy();
    map = null;
  }
  map = new AMap.Map("map", {
    viewMode: "3D",
    pitch: 30,
    zoom: 7, //初始化地图层级
    center: [111.618781, 35.313948], //初始化地图中心点
    features: ["bg", "road"], //设置地图上显示的元素种类,支持'bg'（地图背景）、'point'（POI点）、'road'（道路）、'building'（建筑物）
    mapStyle: "amap://styles/grey"
  });
  // 设置显示卫星图;
  let Satellite = new AMap.TileLayer.Satellite({
    zIndex: 5
  });
  map.add(Satellite);

  initData();
};

/**
 * @description 转换地理坐标数据为three坐标数据
 */
const initData = () => {
  shanxi.features.forEach((item) => {
    item.geometry.coordinates.forEach((v) => {
      const target = v[0][0] instanceof Array ? v[0] : v;
      const path = map.customCoords.lngLatsToCoords(target);
      shanxi_paths.push(path); //缓存起来，供多处使用
    });
  });
  shanxiAll.features.forEach((item) => {
    item.geometry.coordinates.forEach((v) => {
      const target = v[0][0] instanceof Array ? v[0] : v;
      const path = map.customCoords.lngLatsToCoords(target);
      shanxiAll_paths.push(path); //缓存起来，供多处使用
    });
  });
  initLayer();
};

const initLayer = () => {
  // 创建 GL 图层
  let gllayer = new AMap.GLCustomLayer({
    // 图层的层级
    zIndex: 9999,
    visible: true,
    // 初始化的操作，创建图层过程中执行一次。
    init: (gl) => {
      initThree(gl);
    },
    render: () => {
      // 重新设置图层的渲染中心点，将模型等物体的渲染中心点重置
      // 否则和 LOCA 可视化等多个图层能力使用的时候会出现物体位置偏移的问题
      //customCoords.setCenter([117.058737302200598, 36.636412736588625]);
      let { near, far, fov, up, lookAt, position } = map.customCoords.getCameraParams();
      camera.near = near;
      camera.far = far;
      camera.fov = fov;
      camera.position.set(...position);
      camera.up.set(...up);
      camera.lookAt(...lookAt);
      camera.updateProjectionMatrix();
      renderer.render(scene, camera);
      // 这里必须执行！！重新设置 three 的 gl 上下文状态。
      renderer.resetState();
    }
  });
  map.add(gllayer);
};

const initThree = (gl) => {
  // 这里我们的地图模式是 3D，所以创建一个透视相机，相机的参数初始化可以随意设置，因为在 render 函数中，每一帧都需要同步相机参数，因此这里变得不那么重要。
  // 如果你需要 2D 地图（viewMode: '2D'），那么你需要创建一个正交相机
  camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 100, 1 << 30);
  renderer = new THREE.WebGLRenderer({
    context: gl, // 地图的 gl 上下文
    //alpha: true,
    antialias: true // 抗锯齿，默认false 耗性能
    // canvas: gl.canvas,
  });

  // 自动清空画布这里必须设置为 false，否则地图底图将无法显示
  renderer.autoClear = false;
  scene = new THREE.Scene();
  // 环境光照和平行光
  let aLight = new THREE.AmbientLight(0x404040);
  scene.add(aLight);
  drawOneArea(shanxi_paths[0], { height: 10000 });
  drawSide(shanxi_paths[0], { height: 10000 });
  // shanxiAll_paths.forEach((item) => {
  //   drawBorder(item, { height: 11000 });
  // });
};

/**
 *@description绘制单个区块
 *@param{Array} path区块边界数据
 *@param{Object} option配置项
 *@param{Number} option.height区块高度
 *@param{String} option.name区块名称
 */
const drawOneArea = (path, { height }) => {
  // 将GeoJSON数据转换为Three.js可用的几何体格式
  let minX, minY, maxX, maxY;
  const shape = new THREE.Shape();
  path.forEach(([x, y], index) => {
    minX = minX == null || x < minX ? x : minX;
    minY = minY == null || y < minY ? y : minY;
    maxX = maxX == null || x > maxX ? x : maxX;
    maxY = maxY == null || y > maxY ? y : maxY;
    if (index === 0) {
      shape.moveTo(x, y);
    } else {
      shape.lineTo(x, y);
    }
  });
  const geometry = new THREE.ShapeGeometry(shape);

  let uvs = []; // 存储UV坐标
  path.forEach(([x, y]) => {
    // 根据顶点坐标生成UV坐标
    let u = (x - minX) / (maxX - minX); // 根据顶点在x轴上的位置计算u值
    let v = (y - minY) / (maxY - minY); // 根据顶点在y轴上的位置计算v值

    // 将UV坐标添加到数组中
    uvs.push(u, v);
  });
  // 将UV坐标数据存储在Float32Array数组中
  geometry.setAttribute("uv", new THREE.BufferAttribute(new Float32Array(uvs), 2));

  let texture = new THREE.TextureLoader().load("/img/test.png");
  const material = new THREE.MeshBasicMaterial({
    side: THREE.DoubleSide,
    map: texture
    // wireframe: true
    // color: "#000000",
    // opacity: 0.2,
    // transparent: true
  });

  // 使用自定义材质，创建模型对象
  const mesh = new THREE.Mesh(geometry, material);
  // 调整模型海拔高度
  mesh.position.z = height || 0;
  // 加入到场景
  scene.add(mesh);
};

/**
 * @description 创建单个区块侧面
 * @param {Array} path 区块边界数据
 * @param {Object} option 配置项
 * @param {Number} option.height 区块高度
 * @param {String} option.name 区块名称
 */
const drawSide = (path, { height = 0 }) => {
  if (height <= 0) {
    return;
  }
  const arr = path;
  // 保持闭合路线
  if (arr[0].toString() !== arr[arr.length - 1].toString()) {
    arr.push(arr[0]);
  }

  const vec3List = []; // 顶点数组
  let faceList = []; // 三角面数组
  let faceVertexUvs = []; // 面的UV层队列，用于纹理和几何信息映射

  const t0 = [0, 0];
  const t1 = [1, 0];
  const t2 = [1, 1];
  const t3 = [0, 1];

  for (let i = 0; i < arr.length; i++) {
    const [x1, y1] = arr[i];
    vec3List.push([x1, y1, 0]);
    vec3List.push([x1, y1, height]);
  }

  for (let i = 0; i < vec3List.length - 2; i++) {
    if (i % 2 === 0) {
      // 下三角
      faceList = [...faceList, ...vec3List[i], ...vec3List[i + 2], ...vec3List[i + 1]];
      // UV
      faceVertexUvs = [...faceVertexUvs, ...t0, ...t1, ...t3];
    } else {
      // 上三角
      faceList = [...faceList, ...vec3List[i], ...vec3List[i + 1], ...vec3List[i + 2]];
      // UV
      faceVertexUvs = [...faceVertexUvs, ...t3, ...t1, ...t2];
    }
  }

  const geometry = new THREE.BufferGeometry();
  // 顶点三角面
  geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(faceList), 3));
  // UV面
  geometry.setAttribute("uv", new THREE.BufferAttribute(new Float32Array(faceVertexUvs), 2));

  const material = new THREE.MeshBasicMaterial({
    color: "#006600",
    side: THREE.DoubleSide,
    transparent: true,
    depthWrite: true
    // needsUpdate: true,
  });

  if (_conf.sideMap) {
    const texture = new THREE.TextureLoader().load(
      _conf.sideMap,
      () => {},
      null,
      () => {
        console.error("sideMap load error");
      }
    );
    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;
    texture.offset.set(0, 1);
    material.map = texture;
  }
  const sideMesh = new THREE.Mesh(geometry, material);
  scene.add(sideMesh);
};

/**
 *@description创建单个区块的高亮边界线
 *@param{Array} path区块边界数据
 *@param{Object} option配置项
 *@param{Number} option.height区块高度
 *@param{String} option.name区块名称
 */
// const drawBorder = (path, { height }) => {
//   const points = path.map(([x, y]) => {
//     return new THREE.Vector3(x, y, height * 1.01);
//   });

//   const line = new MeshLineGeometry();
//   line.setPoints(points);
//   const mesh = new THREE.Mesh(line, getBorderMaterial());
//   scene.add(mesh);
// };

// 获取边界线材质
// const getBorderMaterial = () => {
//   if (_borderMaterial == null) {
//     let texture;

//     const { borderWidth, borderColor, borderMap } = _conf;

//     // texture.repeat.set(0.1, 0.1)

//     const material = new MeshLineMaterial({
//       lineWidth: borderWidth,
//       sizeAttenuation: 1,
//       useMap: borderMap ? 1 : 0,
//       opacity: 1,
//       transparent: false
//     });

//     if (borderMap) {
//       // 有材质，则使用材质
//       texture = new THREE.TextureLoader().load(
//         _conf.borderMap,
//         () => {},
//         null,
//         () => {
//           console.error("borderMap load error");
//         }
//       );
//       texture.wrapS = THREE.RepeatWrapping;
//       texture.wrapT = THREE.RepeatWrapping;

//       material.map = texture;
//     } else {
//       // 无材质，直接填色
//       material.color = new THREE.Color(borderColor);
//     }

//     _borderMaterial = material;
//   }
//   return _borderMaterial;
// };
</script>

<style scoped lang="scss">
.map-container {
  width: 100%;
  height: 100%;
  position: relative;
  .map {
    width: 100%;
    height: 100%;
  }
  .carScene {
    width: 100%;
    height: 100%;
    position: absolute;
    z-index: 100;
  }
}
</style>
